#include <linux/list.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include "HBA_shm.h"
#include "common.h"

DEFINE_RWLOCK(SCSI_HOST_LIST_LOCK);

typedef struct tagScsiHostList ScsiHostListNode;
struct tagScsiHostList {
	struct list_head node;
	struct Scsi_Host *shost;
};

static LIST_HEAD(g_hostList);

int AddHost(struct Scsi_Host *shost)
{
    ScsiHostListNode *hostNode;
    TP_CHECK_EXPRESSION(shost == NULL, return TP_ERROR);
    write_lock(&SCSI_HOST_LIST_LOCK);
    hostNode = kmalloc(sizeof(ScsiHostListNode), GFP_KERNEL);
    TP_CHECK_EXPRESSION(hostNode == NULL, return TP_ERROR);
    INIT_LIST_HEAD(&hostNode->node);
    hostNode->shost = shost;
    list_add(&hostNode->node, &g_hostList);
    write_unlock(&SCSI_HOST_LIST_LOCK);
    return TP_OK;
}

// 只是从链表中删除，并不会释放内存
int RemoveHost(struct Scsi_Host *shost)
{
    ScsiHostListNode *posNode, nextNode;
    TP_CHECK_EXPRESSION(shost == NULL, return TP_ERROR);
    write_lock(&SCSI_HOST_LIST_LOCK);
    list_for_each_entry_safe(posNode, nextNode, &g_hostList, node) {
        if (posNode->shost == shost) {
            list_del(posNode);
            break;
        }
    }
    write_unlock(&SCSI_HOST_LIST_LOCK);
    return TP_OK;
}

static int CheckHostEqual(const struct Scsi_Host *srcHost, const struct Scsi_Host *dstHost) {
	if (dstHost->max_cmd_len == srcHost->max_cmd_len &&
        dstHost->can_queue == srcHost->can_queue &&
        dstHost->cmd_per_lun == srcHost->cmd_per_lun &&
        dstHost->sg_tablesize == srcHost->sg_tablesize &&
        dstHost->sg_prot_tablesize == srcHost->sg_prot_tablesize &&
        dstHost->max_sectors == srcHost->max_sectors &&
        dstHost->dma_boundary == srcHost->dma_boundary) {
            return TP_OK;
    }
    return TP_ERROR;
}

// 检查shost是否在list中
struct Scsi_Host *FindHost(struct Scsi_Host *shost)
{
    ScsiHostListNode *posNode;
    struct Scsi_Host *posShost;
    int findFlag = TP_ERROR;

    TP_CHECK_EXPRESSION(shost == NULL, return NULL);
    read_lock(&SCSI_HOST_LIST_LOCK);
    list_for_each_entry(posNode, &g_hostList, node) {
        posShost = posNode->shost;
        if (CheckHostEqual(posShost, shost) == TP_OK) {
            findFlag = TP_OK;
            break;
        }
    }
    read_unlock(&SCSI_HOST_LIST_LOCK);
    if (findFlag == TP_OK) {
        return posShost;
    }
    return NULL;
}

struct scsi_device *FindDevice(struct Scsi_Host *shost, uint channel, uint target, u64 lun)
{
    struct scsi_device *sdev;
    shost_for_each_device(sdev, shost) {
        if (sdev->channel == channel && sdev->id = target && sdev->lun == lun) {
            return sdev;
        }
    }
    return NULL;
}
